package com.chujian.framework.core.base;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.chujian.framework.annotation.echo.UseEcho;
import com.chujian.framework.core.IId;
import com.chujian.framework.core.IdGenerator;
import com.chujian.framework.util.mybatis.MybatisIdUs;
import com.chujian.framework.util.mybatis.MybatisUs;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * @author ：chujian
 * @since ：2020-07-15
 * Service层基类
 */
public interface BaseService<T extends IId> extends IService<T> {

    // 缺省空的ID
    Long DEFAULT_NULL_ID = -1L;

    String CHANGE_TYPE_INS = "INSERT";
    String CHANGE_TYPE_UPD = "UPDATE";
    String CHANGE_TYPE_DEL = "DELETE";

    String LIMIT_1_SQL = "limit 1";
    String FOR_UPDATE_SQL = "for update";

    /**
     * 默认按照ID倒序
     */
    String DEFAULT_ORDER_ID_DESC_COL = "##ID_DESC";
    String ORDER_BY_ORDER_NO_ASC = "order_no asc";

    /**
     * 锁定行数数据
     *
     * @param id    id
     * @param clazz clazz
     * @return 是否成功
     */
    default boolean forUpdate(Serializable id, Class<T> clazz) {
        String pkCol = MybatisUs.getEntityPkColName(clazz);
        QueryWrapper<T> wrapper = new QueryWrapper<T>()
                .select("1 as lock_id")
                .eq(pkCol, id)
                .last(FOR_UPDATE_SQL);
        this.getOne(wrapper);
        return true;
    }

    /**
     * 通过ID获取一行数据
     *
     * @param id ID
     * @return 数据对象（带翻译）
     */
    @UseEcho
    default T getByIdAndFull(Serializable id) {
        T data = getBaseMapper().selectById(id);
        full(Collections.singletonList(data));
        return data;
    }

    /**
     * 填充、翻译数据
     *
     * @param datas 数据列表
     */
    default void full(List<T> datas) {
    }

    /**
     * 获取列表
     *
     * @param data   条件对象
     * @param params 请求参数
     * @return 列表数据
     */
    @UseEcho
    default List<T> listAndFull(T data, Map<String, String> params) {
        QueryWrapper<T> wrapper = MybatisUs.toSmartQueryWrapper(data);
        setQueryOrder(wrapper, data.getClass(), getDefaultListOrderCol());
        beforeBaseListAndFull(wrapper);
        return this.listAndFull(wrapper);
    }


    /**
     * 获取list默认的排序列
     *
     * @return true:按ID倒序
     */
    default String getDefaultListOrderCol() {
        return getDefaultOrderCol();
    }

    /**
     * 通用list之前查询
     *
     * @param wrapper wrapper
     */
    default void beforeBaseListAndFull(QueryWrapper<T> wrapper) {
    }

    /**
     * list并且填充
     *
     * @param wrapper wrapper
     * @return List<T>
     */
    @UseEcho
    default List<T> listAndFull(Wrapper<T> wrapper) {
        List<T> list = this.list(wrapper);
        this.full(list);
        return list;
    }

    /**
     * 获取分页数据
     *
     * @param current 页码
     * @param size    每页行数
     * @param data    条件对象
     * @param params  请求参数
     * @return 页数据
     */
    @UseEcho
    default IPage<T> pageAndFull(Integer current, Integer size, T data, Map<String, String> params) {
        QueryWrapper<T> wrapper = MybatisUs.toSmartQueryWrapper(data);
        setQueryOrder(wrapper, data.getClass(), getDefaultPageOrderCol());
        beforeBasePageAndFull(wrapper);
        return this.pageAndFull(current, size, wrapper);
    }

    /**
     * 设置排序列
     *
     * @param wrapper
     * @param entityClass
     * @param orderCol
     */
    default void setQueryOrder(QueryWrapper<T> wrapper, Class<?> entityClass, String orderCol) {
        if (StringUtils.isEmpty(orderCol)) return;
        // 按ID默认倒序
        if (DEFAULT_ORDER_ID_DESC_COL.equals(orderCol)) {
            wrapper.orderByDesc(MybatisUs.getEntityPkColName(entityClass));
            return;
        }
        // 自定义排序
        String[] split = orderCol.split(" ");
        if (split.length == 1 || MybatisUs.ORDER_ASC.equalsIgnoreCase(split[1])) {
            wrapper.orderByAsc(split[0]);
        } else {
            wrapper.orderByDesc(split[0]);
        }
    }

    /**
     * 获取page默认的排序列
     *
     * @return true:按ID倒序
     */
    default String getDefaultPageOrderCol() {
        return getDefaultOrderCol();
    }

    /**
     * 缺省排序列
     *
     * @return
     */
    default String getDefaultOrderCol() {
        return DEFAULT_ORDER_ID_DESC_COL;
    }

    /**
     * 通用page之前执行
     *
     * @param wrapper wrapper
     */
    default void beforeBasePageAndFull(QueryWrapper<T> wrapper) {
    }

    /**
     * 获取分页数据
     *
     * @param current 页码
     * @param size    每页行数
     * @param wrapper 条件
     * @return 页数据
     */
    @UseEcho
    default IPage<T> pageAndFull(Integer current, Integer size, Wrapper<T> wrapper) {
        IPage<T> page = this.page(new Page<>(current, size), wrapper);
        this.full(page.getRecords());
        return page;
    }


    /**
     * in查询
     *
     * @param ids ids
     * @return List<T>
     */
    @UseEcho
    default List<T> inAndFull(List<Long> ids) {
        List<T> list = this.listByIds(ids);
        this.full(list);
        return list;
    }

    /**
     * 保存一行数据
     *
     * @param data 数据对象
     * @return 保存后的对象
     */
    @UseEcho
    @Transactional(rollbackFor = Exception.class)
    default T ins(T data) {
        if (!beforeChange(CHANGE_TYPE_INS, data)) return null;
        if (!beforeIns(data)) return null;
        fullId(data);
        this.save(data);
        T saveData = this.getByIdAndFull(data.getId());
        afterIns(data, saveData);
        afterChange(CHANGE_TYPE_INS, data, data, saveData);
        return saveData;
    }

    /**
     * 修改一行数据
     *
     * @param data 数据对象
     * @return 修改后的对象
     */
    @UseEcho
    @Transactional(rollbackFor = Exception.class)
    default T upd(T data) {
        if (!beforeChange(CHANGE_TYPE_UPD, data)) return null;
        if (!beforeUpd(data)) return null;
        T oldData = recordUpdOldValue() ? this.getByIdAndFull(data.getId()) : null;
        this.updateById(data);
        T newData = this.getByIdAndFull(data.getId());
        afterUpd(data, oldData, newData);
        afterChange(CHANGE_TYPE_UPD, data, oldData, newData);
        return newData;
    }

    /**
     * update前是否记录旧值
     *
     * @return true记录旧值
     */
    default boolean recordUpdOldValue() {
        return false;
    }

    /**
     * 生成ID
     *
     * @param data 数据对象
     */
    default void fullId(T data) {
        if (data.getId() == null && IdType.INPUT == MybatisIdUs.getIdType(data.getClass())) {
            data.setId(IdGenerator.genId());
        }
    }

    /**
     * insert/save前执行
     *
     * @param paramsData 传入的参数对象
     * @return false:阻止插入 true:继续插入
     */
    default boolean beforeIns(T paramsData) {
        return true;
    }

    /**
     * 保存后执行
     *
     * @param paramsData 传入的参数对象
     * @param data       新增后产生的对象
     */
    default void afterIns(T paramsData, T data) {

    }

    /**
     * 修改前执行
     *
     * @param paramsData 数据对象
     * @return false:阻止修改 true:继续修改
     */
    default boolean beforeUpd(T paramsData) {
        return true;
    }

    /**
     * 修改后执行
     *
     * @param paramsData 传入的参数对象
     * @param oldData    旧值
     * @param newData    新值
     */
    default void afterUpd(T paramsData, T oldData, T newData) {
    }

    /**
     * 数据变更前
     *
     * @param changeType 改变类型 CHANGE_TYPE
     * @param paramsData 传入的参数对象
     * @return false阻止执行 true放行
     */
    default boolean beforeChange(String changeType, Object paramsData) {
        return true;
    }

    /**
     * 数据变更后
     *
     * @param changeType 改变类型 CHANGE_TYPE
     * @param paramsData 传入的参数对象
     * @param oldValue   旧值
     * @param newValue   新值
     */
    default void afterChange(String changeType, Object paramsData, Object oldValue, Object newValue) {
    }

    /**
     * 删除一行数据
     *
     * @param id id
     * @return true删除成功
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean del(Long id) {
        return this.dels(CollUtil.newArrayList(id));
    }

    /**
     * 删除多行
     *
     * @param ids ids
     * @return true删除成功
     */
    @Transactional(rollbackFor = Exception.class)
    default boolean dels(List<Long> ids) {
        if (!beforeChange(CHANGE_TYPE_DEL, ids)) return false;
        if (!beforeDel(ids)) return false;
        boolean b = this.removeByIds(ids);
        afterDel(ids);
        afterChange(CHANGE_TYPE_DEL, ids, ids, ids);
        return b;
    }

    /**
     * 删除之前执行
     *
     * @param ids ids
     * @return false阻止删除 true放行
     */
    default boolean beforeDel(List<Long> ids) {
        return true;
    }

    /**
     * 删除之后执行
     *
     * @param ids ids
     */
    default void afterDel(List<Long> ids) {

    }

}
